ποΈGitΠ―ΡΠ°ποΈ
Node / meshtastic / Meshtastic-Android / files / .specify / extensions / review / .github / workflows / ci.yml
Displaying Raw β’ Download
.specify/extensions/review/.github/workflows/ci.yml 4e48e64e786b58d7c73280f7aa6ebf55be9e53e5 (4e48e64e) Text, 8.62 KB
Tff7b72nameTb4b4b4: Ta5d6ffCI
Tff7b72onTb4b4b4:
Tff7b72pushTb4b4b4:
Tff7b72branchesTb4b4b4: Tb4b4b4[Te6edf3mainTb4b4b4]
Tff7b72pull_requestTb4b4b4:
Tff7b72branchesTb4b4b4: Tb4b4b4[Te6edf3mainTb4b4b4]
Tff7b72permissionsTb4b4b4:
Tff7b72contentsTb4b4b4: Ta5d6ffread
Tff7b72jobsTb4b4b4:
Tff7b72validate-manifestTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffValidateTa5d6ff Ta5d6ffManifestTa5d6ff Ta5d6ff&Ta5d6ff Ta5d6ffCommands
Tff7b72runs-onTb4b4b4: Ta5d6ffubuntu-latest
Tff7b72stepsTb4b4b4:
Tb4b4b4- Tff7b72usesTb4b4b4: Ta5d6ffactions/checkout@v7.0.0
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffCheckTa5d6ff Ta5d6ffrequiredTa5d6ff Ta5d6fffiles
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72files=("README.md" "CHANGELOG.md" "extension.yml" "LICENSE")
Tff7b72for f in "${files[@]}"; do
Tff7b72if [ -f "$f" ]; then
Tff7b72echo "β
$f exists"
Tff7b72else
Tff7b72echo "β $f missing"
Tff7b72exit 1
Tff7b72fi
Tff7b72done
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffValidateTa5d6ff Ta5d6ffextension.yml
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72# Validate YAML syntax
Tff7b72python3 -c "import yaml; yaml.safe_load(open('extension.yml'))" || {
Tff7b72echo "β extension.yml has invalid YAML syntax"
Tff7b72exit 1
Tff7b72}
Tff7b72echo "β
extension.yml has valid YAML syntax"
Tff7b72# Deep manifest validation
Tff7b72python3 << 'PYEOF'
Tff7b72import yaml, sys, re, os
Tff7b72with open('extension.yml') as f:
Tff7b72ext = yaml.safe_load(f)
Tff7b72errors = []
Tff7b72# --- Top-level required fields ---
Tff7b72for field in ['schema_version', 'extension']:
Tff7b72if field not in ext:
Tff7b72errors.append(f"Missing required field: {field}")
Tff7b72if 'extension' not in ext:
Tff7b72for e in errors:
Tff7b72print(f"β {e}")
Tff7b72sys.exit(1)
Tff7b72meta = ext['extension']
Tff7b72for field in ['id', 'name', 'version', 'description', 'author']:
Tff7b72if field not in meta:
Tff7b72errors.append(f"Missing extension.{field}")
Tff7b72# --- Extension ID format (lowercase, alphanumeric, hyphens) ---
Tff7b72ext_id = meta.get('id', '')
Tff7b72if ext_id and not re.match(r'^[a-z0-9-]+$', ext_id):
Tff7b72errors.append(f"Extension ID '{ext_id}' must match ^[a-z0-9-]+$ (lowercase, alphanumeric, hyphens only)")
Tff7b72if ext_id:
Tff7b72print(f"β
Extension ID '{ext_id}' has valid format")
Tff7b72# --- SemVer validation ---
Tff7b72version = meta.get('version', '')
Tff7b72if version and not re.match(r'^\d+\.\d+\.\d+$', version):
Tff7b72errors.append(f"Version '{version}' must follow semantic versioning (X.Y.Z)")
Tff7b72if version:
Tff7b72print(f"β
Version '{version}' is valid SemVer")
Tff7b72# --- Description length (under 200 chars) ---
Tff7b72desc = meta.get('description', '')
Tff7b72if len(desc) > 200:
Tff7b72errors.append(f"Description is {len(desc)} characters (must be under 200)")
Tff7b72if desc:
Tff7b72print(f"β
Description is {len(desc)} characters (under 200 limit)")
Tff7b72# --- requires.speckit_version ---
Tff7b72requires = ext.get('requires', {})
Tff7b72if not requires or 'speckit_version' not in requires:
Tff7b72errors.append("Missing requires.speckit_version (required)")
Tff7b72else:
Tff7b72print(f"β
requires.speckit_version: {requires['speckit_version']}")
Tff7b72# --- provides.commands exists and is non-empty ---
Tff7b72provides = ext.get('provides', {})
Tff7b72commands = provides.get('commands', [])
Tff7b72if not commands:
Tff7b72errors.append("provides.commands must exist and contain at least one command")
Tff7b72else:
Tff7b72print(f"β
provides.commands has {len(commands)} command(s)")
Tff7b72cmd_pattern = f'^speckit\\.{re.escape(ext_id)}\\.[a-z0-9-]+$'
Tff7b72# --- Command naming convention: speckit.{ext-id}.{command} ---
Tff7b72for cmd in commands:
Tff7b72name = cmd.get('name', '')
Tff7b72if not re.match(cmd_pattern, name):
Tff7b72errors.append(f"Command name '{name}' must match pattern speckit.{ext_id}.<command>")
Tff7b72else:
Tff7b72print(f"β
Command '{name}' follows naming convention")
Tff7b72# --- Alias naming convention: speckit.{ext-id}.{alias} ---
Tff7b72for cmd in commands:
Tff7b72aliases = cmd.get('aliases', [])
Tff7b72if aliases is None:
Tff7b72aliases = []
Tff7b72if not isinstance(aliases, list):
Tff7b72errors.append(f"Aliases for command '{cmd.get('name', '')}' must be a list")
Tff7b72continue
Tff7b72for alias in aliases:
Tff7b72if not isinstance(alias, str):
Tff7b72errors.append(f"Alias in command '{cmd.get('name', '')}' must be a string")
Tff7b72continue
Tff7b72if not re.match(cmd_pattern, alias):
Tff7b72errors.append(f"Alias '{alias}' must match pattern speckit.{ext_id}.<command>")
Tff7b72else:
Tff7b72print(f"β
Alias '{alias}' follows naming convention")
Tff7b72# --- Command file existence ---
Tff7b72for cmd in commands:
Tff7b72cmd_file = cmd.get('file', '')
Tff7b72if cmd_file and not os.path.isfile(cmd_file):
Tff7b72errors.append(f"Command file '{cmd_file}' referenced in manifest does not exist")
Tff7b72elif cmd_file:
Tff7b72print(f"β
Command file '{cmd_file}' exists")
Tff7b72# --- Config template existence ---
Tff7b72configs = provides.get('config', [])
Tff7b72for cfg in configs:
Tff7b72template = cfg.get('template', '')
Tff7b72if template and not os.path.isfile(template):
Tff7b72errors.append(f"Config template '{template}' referenced in manifest does not exist")
Tff7b72elif template:
Tff7b72print(f"β
Config template '{template}' exists")
Tff7b72# --- Report ---
Tff7b72if errors:
Tff7b72print()
Tff7b72for e in errors:
Tff7b72print(f"β {e}")
Tff7b72sys.exit(1)
Tff7b72print()
Tff7b72print(f"β
All manifest validations passed")
Tff7b72print(f" Extension: {meta['name']} v{meta['version']}")
Tff7b72PYEOF
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffValidateTa5d6ff Ta5d6ffcommandTa5d6ff Ta5d6fffiles
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72if [ -d commands ]; then
Tff7b72for cmd in commands/*.md; do
Tff7b72if [ -f "$cmd" ]; then
Tff7b72# Check frontmatter exists
Tff7b72if head -1 "$cmd" | grep -q "^---"; then
Tff7b72echo "β
$cmd has frontmatter"
Tff7b72# Check description field in frontmatter
Tff7b72if python3 -c "
Tff7b72import yaml, sys
Tff7b72with open('$cmd') as f:
Tff7b72content = f.read()
Tff7b72parts = content.split('---', 2)
Tff7b72if len(parts) >= 3:
Tff7b72fm = yaml.safe_load(parts[1])
Tff7b72if fm and 'description' in fm:
Tff7b72sys.exit(0)
Tff7b72sys.exit(1)
Tff7b72" 2>/dev/null; then
Tff7b72echo "β
$cmd has 'description' in frontmatter"
Tff7b72else
Tff7b72echo "β $cmd missing 'description' in frontmatter (required)"
Tff7b72exit 1
Tff7b72fi
Tff7b72else
Tff7b72echo "β $cmd missing frontmatter"
Tff7b72exit 1
Tff7b72fi
Tff7b72fi
Tff7b72done
Tff7b72else
Tff7b72echo "β No commands directory found"
Tff7b72exit 1
Tff7b72fi
Tff7b72validate-bashTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffValidateTa5d6ff Ta5d6ff&Ta5d6ff Ta5d6ffTestTa5d6ff Ta5d6ffBashTa5d6ff Ta5d6ffScriptsTa5d6ff Ta5d6ff(${{Ta5d6ff Ta5d6ffmatrix.osTa5d6ff Ta5d6ff}})
Tff7b72runs-onTb4b4b4: Ta5d6ff${{Ta5d6ff Ta5d6ffmatrix.osTa5d6ff Ta5d6ff}}
Tff7b72strategyTb4b4b4:
Tff7b72matrixTb4b4b4:
Tff7b72osTb4b4b4: Tb4b4b4[Te6edf3ubuntu-latestTb4b4b4, Te6edf3macos-latestTb4b4b4]
Tff7b72stepsTb4b4b4:
Tb4b4b4- Tff7b72usesTb4b4b4: Ta5d6ffactions/checkout@v7.0.0
Tff7b72withTb4b4b4:
Tff7b72submodulesTb4b4b4: Ta5d6ffrecursive
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffCheckTa5d6ff Ta5d6ffBashTa5d6ff Ta5d6ffscriptsTa5d6ff Ta5d6ffsyntax
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72echo "Bash version: $(bash --version | head -1)"
Tff7b72for script in scripts/bash/*.sh; do
Tff7b72echo "Checking $script..."
Tff7b72bash -n "$script"
Tff7b72echo " β Syntax OK"
Tff7b72done
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffRunTa5d6ff Ta5d6ffBATSTa5d6ff Ta5d6fftests
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72echo "Running BATS tests on ${{ matrix.os }}..."
Tff7b72tests/bats/lib/bats-core/bin/bats tests/bats/*.bats
Tff7b72validate-powershellTb4b4b4:
Tff7b72nameTb4b4b4: Ta5d6ffValidateTa5d6ff Ta5d6ff&Ta5d6ff Ta5d6ffTestTa5d6ff Ta5d6ffPowerShellTa5d6ff Ta5d6ffScripts
Tff7b72runs-onTb4b4b4: Ta5d6ffwindows-latest
Tff7b72stepsTb4b4b4:
Tb4b4b4- Tff7b72usesTb4b4b4: Ta5d6ffactions/checkout@v7.0.0
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffCheckTa5d6ff Ta5d6ffPowerShellTa5d6ff Ta5d6ffscriptsTa5d6ff Ta5d6ffsyntax
Tff7b72shellTb4b4b4: Ta5d6ffpwsh
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72foreach ($script in Get-ChildItem scripts/powershell/*.ps1) {
Tff7b72Write-Host "Checking $($script.Name)..."
Tff7b72$null = [System.Management.Automation.PSParser]::Tokenize(
Tff7b72(Get-Content $script.FullName -Raw), [ref]$null
Tff7b72)
Tff7b72Write-Host " OK"
Tff7b72}
Tb4b4b4- Tff7b72nameTb4b4b4: Ta5d6ffRunTa5d6ff Ta5d6ffPesterTa5d6ff Ta5d6fftests
Tff7b72shellTb4b4b4: Ta5d6ffpwsh
Tff7b72runTb4b4b4: Tb4b4b4|
Tff7b72$config = New-PesterConfiguration
Tff7b72$config.Run.Path = "tests/pester"
Tff7b72$config.Output.Verbosity = "Detailed"
Tff7b72$config.Run.Exit = $true
Tff7b72Invoke-Pester -Configuration $config
Served by rngit 1.5.2 - Generated in 0.04s